package org.fujay.langchain4jserver.application.service.impl;

import cn.hutool.core.util.ObjectUtil;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fujay.langchain4jserver.application.assembler.ModelAssembler;
import org.fujay.langchain4jserver.application.command.CreateModelCommand;
import org.fujay.langchain4jserver.application.command.UpdateModelCommand;
import org.fujay.langchain4jserver.application.dto.ModelConnectionTestResultDTO;
import org.fujay.langchain4jserver.application.dto.ModelDTO;
import org.fujay.langchain4jserver.application.dto.ModelSummaryDTO;
import org.fujay.langchain4jserver.application.service.ModelService;
import org.fujay.langchain4jserver.domain.application.repository.AppRepository;
import org.fujay.langchain4jserver.domain.model.aggregate.Model;
import org.fujay.langchain4jserver.domain.model.exception.ModelInUseException;
import org.fujay.langchain4jserver.domain.model.exception.ModelNotFoundException;
import org.fujay.langchain4jserver.domain.model.repository.ModelRepository;
import org.fujay.langchain4jserver.domain.model.service.ModelProviderService;
import org.fujay.langchain4jserver.domain.model.vo.ModelId;
import org.fujay.langchain4jserver.domain.model.vo.ModelType;
import org.fujay.langchain4jserver.domain.model.vo.TestResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 模型应用服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ModelServiceImpl implements ModelService {

    private final ModelRepository modelRepository;
    private final AppRepository appRepository;
    private final ModelProviderService modelProvider;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ModelDTO createModel(CreateModelCommand command) {
        log.info(
                "创建模型: 名称={}, 类型={}, 供应商={}",
                command.getModelName(),
                command.getModelType(),
                command.getSupplier().supplierCode());
        ModelId modelId = ModelId.create();
        Model model =
                Model.create(
                        modelId,
                        command.getModelName(),
                        ModelType.fromCode(command.getModelType()),
                        command.getSupplier(),
                        command.getConfig());
        modelRepository.save(model);
        log.info("模型创建成功，ID: {}", modelId.value());
        return ModelAssembler.toDTO(model);
    }

    @Override
    public Optional<ModelDTO> getModel(ModelId modelId) {
        log.debug("获取模型详情，ID: {}", modelId.value());
        return modelRepository.findById(modelId).map(ModelAssembler::toDTO);
    }

    @Override
    public List<ModelSummaryDTO> listModels() {
        log.debug("列出所有模型");
        List<Model> models = modelRepository.findAll();
        return ModelAssembler.toSummaryDTOList(models);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<ModelDTO> updateModel(UpdateModelCommand command) {
        log.info("更新模型，ID: {}", command.getModelId().value());
        Model model =
                modelRepository
                        .findById(command.getModelId())
                        .orElseThrow(() -> new ModelNotFoundException(command.getModelId()));

        model.updateBasicInfo(command.getModelName(), command.getModelType(), command.getSupplier());
        model.updateConfig(command.getConfig());

        modelRepository.save(model);
        log.info("模型更新成功，ID: {}", command.getModelId().value());
        return Optional.of(ModelAssembler.toDTO(model));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteModel(ModelId modelId) {
        log.info("准备删除模型，ID: {}", modelId.value());

        long usageCount = appRepository.countByModelId(modelId);
        if (usageCount > 0) {
            log.warn("模型 ID [{}] 正在被 {} 个应用使用，无法删除。", modelId.value(), usageCount);
            throw new ModelInUseException(modelId);
        }

        if (!modelRepository.existsById(modelId)) {
            log.warn("尝试删除一个不存在的模型，ID: {}", modelId.value());
            return;
        }

        modelRepository.deleteById(modelId);
        log.info("模型删除成功，ID: {}", modelId.value());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<ModelDTO> enableModel(ModelId modelId) {
        log.info("启用模型，ID: {}", modelId.value());
        Model model =
                modelRepository.findById(modelId).orElseThrow(() -> new ModelNotFoundException(modelId));
        model.enable();
        modelRepository.save(model);
        log.info("模型启用成功，ID: {}", modelId.value());
        return Optional.of(ModelAssembler.toDTO(model));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Optional<ModelDTO> disableModel(ModelId modelId) {
        log.info("禁用模型，ID: {}", modelId.value());
        Model model =
                modelRepository.findById(modelId).orElseThrow(() -> new ModelNotFoundException(modelId));
        model.disable();
        modelRepository.save(model);
        log.info("模型禁用成功，ID: {}", modelId.value());
        return Optional.of(ModelAssembler.toDTO(model));
    }

    @Override
    public ModelConnectionTestResultDTO testModelConnection(ModelId modelId) {
        log.info("开始测试模型连接，ID: {}", modelId.value());
        Model model =
                modelRepository.findById(modelId).orElseThrow(() -> new ModelNotFoundException(modelId));

        if (ObjectUtil.isNull(model)) {
            return ModelConnectionTestResultDTO.builder()
                    .success(false)
                    .message("模型不存在")
                    .details(Map.of("modelId", modelId.value(), "status", "DISABLED"))
                    .build();
        }

        try {
            if (ModelType.CHAT.equals(model.getModelType())) {
                testChatModelConnection(model);
            } else if (ModelType.EMBEDDING.equals(model.getModelType())) {
                testEmbeddingModelConnection(model);
            } else {
                log.warn("模型 {} 的类型 ({}) 不支持连接测试。", modelId.value(), model.getModelType().name());
                return ModelConnectionTestResultDTO.builder()
                        .success(false)
                        .message("不支持的模型类型进行连接测试")
                        .details(Map.of("modelId", modelId.value(), "type", model.getModelType().name()))
                        .build();
            }

            log.info("模型 {} 连接测试成功。", modelId.value());
            model.recordConnectionTest(TestResult.SUCCESS);
            modelRepository.save(model);
            return ModelConnectionTestResultDTO.builder()
                    .success(true)
                    .message("连接成功")
                    .details(Map.of("modelId", modelId.value(), "status", "SUCCESS"))
                    .build();

        } catch (Exception e) {
            log.error("模型 {} 连接测试失败: {}", modelId.value(), e.getMessage(), e);
            String errorMessage = "连接测试失败: " + e.getMessage();
            model.recordConnectionTest(TestResult.FAILED);
            modelRepository.save(model); // 保存状态变更
            return ModelConnectionTestResultDTO.builder()
                    .success(false)
                    .message(errorMessage)
                    .details(
                            Map.of(
                                    "modelId", modelId.value(), "status", "FAILED", "error", e.getClass().getName()))
                    .build();
        }
    }

    // 辅助方法：测试聊天模型连接
    private void testChatModelConnection(Model model) {
        log.debug("测试聊天模型连接: {}", model.getId().value());
        ChatModel chatModel = modelProvider.getChatModel(model);
        chatModel.chat("你好");
    }

    // 辅助方法：测试嵌入模型连接
    private void testEmbeddingModelConnection(Model model) {
        log.debug("测试嵌入模型连接: {}", model.getId().value());
        EmbeddingModel embeddingModel = modelProvider.getEmbeddingModel(model);
        embeddingModel.embed("测试文本");
    }
}
